-- Set up a namespace for the addon.
LootAuctioneer = {Frame = nil, gItem = nil, gTimer = nil, gTimeElapsed = nil, gUpdatePacer = 0, gIsAuctionRunning = false, gLootRolls = {}, ItemFrameCount = 0, ItemFrame = {}, gChatChannel = "SAY"}

local MAIN_SPEC_ROLL, OFF_SPEC_ROLL = 1,2

function LootAuctioneer:SetChatChannel()
	local numPlayers = GetNumPartyMembers()
	if(numPlayers > 1) then
		self.gChatChannel = UnitInRaid("player") and "RAID" or "PARTY"
	else
		self.gChatChannel = "SAY"
	end
end

-- Addon load handler
function LootAuctioneer:OnLoad()
	self.Frame = LootAuctioneerFrame
	self.Frame:RegisterForDrag("LeftButton")
	self.Frame:RegisterEvent("LOOT_OPENED")
	self.Frame:RegisterEvent("LOOT_SLOT_CLEARED")
	self.Frame:RegisterEvent("LOOT_CLOSED")
	self.Frame:RegisterEvent("PARTY_MEMBERS_CHANGED")
	self:SetChatChannel()
	self.Frame:Hide()
end

-- Addon event handler
function LootAuctioneer:OnEvent(event, ...)
	if(event:find("CHAT")) then
		return self:ChatHandler(event, ...)
	elseif (event:find("LOOT")) then
		return self:LootHandler(event, ...)
	elseif (event == "PARTY_MEMBERS_CHANGED") then
		self:SetChatChannel()
	end
end

-- Update event handler. Used only when timed auction is running.
function LootAuctioneer:OnUpdate(elapsed)

	self.gUpdatePacer = self.gUpdatePacer + elapsed
	if(self.gUpdatePacer < 1) then return end
	self.gUpdatePacer = 0

	if(self.gTimer and self.gIsAuctionRunning) then
		if(not self.gTimeElapsed) then self.gTimeElapsed = 0 end
		self.gTimeElapsed = self.gTimeElapsed + 1
		local itemLink = self.gItem and " for " .. self.gItem .. "." or "."
		for i = 0,5,1 do
			if(self.gTimeElapsed + i > tonumber(self.gTimer)) then
				if(i == 0) then
					SendChatMessage("Auction Timer Expired. Closing Auction" .. itemLink, self.gChatChannel)
					self:FinishLootAuction()
				else
					SendChatMessage("Auction timer expiring in " .. i .. " seconds " .. itemLink, self.gChatChannel)
				end
				break
			end
		end
	end
end

-- These functions dynamically register and unregister chat evevnts. This lowers the overall processing footprint of the app.
function LootAuctioneer:RegisterChatHandles()
	self.Frame:RegisterEvent("CHAT_MSG_PARTY")
	self.Frame:RegisterEvent("CHAT_MSG_RAID")
	self.Frame:RegisterEvent("CHAT_MSG_WHISPER")
	self.Frame:RegisterEvent("CHAT_MSG_RAID_LEADER")
end

function LootAuctioneer:UnregisterChatHandles()
	self.Frame:UnregisterEvent("CHAT_MSG_PARTY")
	self.Frame:UnregisterEvent("CHAT_MSG_RAID")
	self.Frame:UnregisterEvent("CHAT_MSG_WHISPER")
	self.Frame:UnregisterEvent("CHAT_MSG_RAID_LEADER")
end

-- ChatHandler
-- ===========
-- Addon event handler for all chat-based API events.
function LootAuctioneer:ChatHandler(event, ...)
	local msg, author, response, responseitem = arg1, arg2, nil, nil
	msg = msg:upper()

	if(self.gItem) then
		responseitem = " for " .. self.gItem .. "."
	else
		responseitem = "."
	end

	if(msg:find("MAIN")) then
		self.gLootRolls[author] = MAIN_SPEC_ROLL
		response = "LOOTAUCTIONEER: You have been entered as a main spec roller" .. responseitem .. " Reply with REMOVE in message to be removed from list."
	elseif(msg:find("OFF")) then
		self.gLootRolls[author] = OFF_SPEC_ROLL
		response = "LOOTAUCTIONEER: You have been entered as an off spec roller for " .. responseitem .. " Reply with REMOVE in message to be removed from list."
	elseif(msg:find("REMOVE")) then
		self.gLootRolls[author] = nil
		response = "LOOTAUCTIONEER: You have been been removed from the roll list" .. responseitem
	elseif(msg:find("HELP")) then
		response = "LOOTAUCTIONEER: Reply with MAIN for main spec list, OFF for off spec list, or REMOVE to remove from all roll lists."
	end

	if(response) then
		if(author ~= UnitName("player")) then
			SendChatMessage(response, WHISPER, nil, author)
		else
			print(response)
		end
	end
end


-- LootHandler
-- ===========
-- Addon event handler for all loot-based API events.
function LootAuctioneer:LootHandler(event, ...)
	if(event == "LOOT_OPENED") then
		local numItems = GetNumLootItems()
		for i=1,numItems do
			local _,item,quantity,quality,_ = GetLootSlotInfo(i)
			--self:AddItemFrame(i, item, quantity)
		end
		--self.Frame:Show()
	elseif(event == "LOOT_CLOSED") then
		self.Frame:Hide()
	elseif(event == "LOOT_SLOT_CLEARED") then
		--self.Frame:Show()
	end
end

-- GetHighestToLowest
-- ==================
-- Iterator used to grab results from a key-value table in order of largest value to smallest value.
function GetHighestToLowest(table)
	local currentBiggest, currentBiggestVal, lastIndex, lastIndexVal = nil
	return function()
		currentBiggest = nil
		currentBiggestVal = nil
		for i,v in pairs(table) do
			-- Tests for two cases:
				-- 1) On entry and re-entry, need to make sure we start with a valid value to not put us into an infinite loop.
				-- 2) After the first entry, we need to make sure we find a valid value that is higher than the current highest, but lower/equal to the last value.
			if( not lastIndex and not currentBiggest
				or not lastIndex and (v >= currentBiggestVal)
				or lastIndex and not currentBiggest and (v <= lastIndexVal) and (i ~= lastIndex)
				or lastIndex and currentBiggest and (v >= currentBiggestVal) and (v <= lastIndexVal) and (lastIndex ~= i) )
				then
				currentBiggest = i
				currentBiggestVal = v
			end
		end
		lastIndex = currentBiggest
		lastIndexVal = currentBiggestVal
		return currentBiggest, currentBiggestVal
	end
end

-- StartLootAuction
-- ================
-- Registers event handlers and sets/resets global variables in preparation for a new auction
function LootAuctioneer:StartLootAuction(item, timer)
	if(self.gIsAuctionRunning) then
		print("Error: There is an auction running. You cannot start an auction.")
		return
	end


	self.gItem = item
	self.gTimer = timer
	self.gTimeElapsed = 0
	self.gLootRolls = {}
	self.gIsAuctionRunning = true

	self:RegisterChatHandles()
	self.Frame:Show()
end


-- FinishLootAuction
-- =============
-- Sorts out the main roll table into a main spec table and off spec table complete with random rolls.
-- Sorts the results and reports them to raid channel.
-- Clears out structures/variables to prepare for a fresh auction.
function LootAuctioneer:FinishLootAuction()
	if(not self.gIsAuctionRunning) then
		print("Error: There is no auction running. You cannot end an auction.")
		return
	end
	self.Frame:Hide()

	self:UnregisterChatHandles()

	local mainRolls, offRolls = {}, {}
	local mainRollsCount, offRollsCount = 0, 0

	for i,v in pairs(self.gLootRolls) do
		rnd = math.random(100)
		if(MAIN_SPEC_ROLL == v) then
			mainRolls[i] = rnd
			mainRollsCount = mainRollsCount + 1
		elseif(OFF_SPEC_ROLL == v) then
			offRolls[i] = rnd
			offRollsCount = offRollsCount + 1
		end
	end

	if(mainRollsCount > 0) then
		SendChatMessage("MAIN SPEC ROLLS", self.gChatChannel)
		SendChatMessage("===============", self.gChatChannel)
		for i,v in GetHighestToLowest(mainRolls) do
			SendChatMessage(string.format("%-20s", i) .. string.format("%3s", v) , self.gChatChannel)
		end
	end

	if(offRollsCount > 0) then
		SendChatMessage("OFF SPEC ROLLS", self.gChatChannel)
		SendChatMessage("==============", self.gChatChannel)
		for i,v in GetHighestToLowest(offRolls) do
			SendChatMessage(string.format("%-20s", i) .. string.format("%3s", v) , self.gChatChannel)
		end
	end

	self.gLootRolls = {}
	self.gIsAuctionRunning = false
	self.gTimer = nil
	self.gTimeElapsed = nil
	self.gItem = nil
end

-- AuctionHandler
-- =============
-- Parses and processes slash commands entered through the chat interface.
function LootAuctioneer:AuctionHandler(msg, editbox)
	local tokens = {}
	for token in msg:gmatch(".[^%s%c]+") do
		table.insert(tokens, token)
	end

	print(unpack(tokens))
	local cmd, item, timer = tokens[1], tokens[2], tokens[3]

	if(not cmd) then
		print("Loot Auctioneer Commands (/la, /lootauctioneer)")
		print("=======================================")
		print("START [ITEM] [TIME] -- Starts an auction for [ITEM] with length [TIME]")
		print("END -- Ends the current auction and displays winners")
		return
	end

	local timerMsg = timer and " for " .. timer .. " seconds" or ""
	local itemMsg = item and " for " .. item or ""

	cmd = cmd:upper()
	if ("START" == cmd and not self.gIsAuctionRunning) then
		print("You told me to start an item auction!")
		SendChatMessage("Starting auction" .. itemMsg .. timerMsg .. ". Type MAIN or OFF to be added to roll list.", self.gChatChannel)
		self:StartLootAuction(item, timer)
	elseif ("END" == cmd and self.gIsAuctionRunning) then
		SendChatMessage("Auction" .. itemMsg .. " ended.", self.gChatChannel)
		print("You told me to end your item auction!")
		self:FinishLootAuction()
	else
		print("Loot Auctioneer Commands (/la, /lootauctioneer)")
		print("=======================================")
		print("START [ITEM] [TIME] -- Starts an auction for [ITEM] with length [TIME]")
		print("END -- Ends the current auction and displays winners")
	end
end

-- AddItemFrame
-- ============
-- Makes sure that there is a LootAuctioneer frame for each item that's on a looted entity. Makes sure to reuse old frames.
function LootAuctioneer:AddItemFrame(id, itemname, quantity)
	if(id > self.ItemFrameCount) then
		--local itemFrame = CreateFrame("Frame", "LootAuctioneerLootFrame"..id, self.Frame)
		local itemFrame = self.Frame:CreateFontString("LootAuctioneerLootFrameText"..id, "OVERLAY", "GameFontHighlight")
		--itemFrame.Text = itemFrameText
		if (id == 1) then
			itemFrame:SetPoint("TOPLEFT", self.Frame, "TOPLEFT", 10, -30)
		else
			itemFrame:SetPoint("TOPLEFT", self.ItemFrame[id-1], "BOTTOMLEFT", 0, -2)
		end
		table.insert(self.ItemFrame, id, itemFrame)
		self.ItemFrameCount = self.ItemFrameCount + 1
	end
	local itemFrame = self.ItemFrame[id]

	itemFrame:SetText(quantity .. "x " .. itemname)
	itemFrame:Show()
	--self.Frame:SetHeight(self.Frame:GetHeight() + itemFrame:GetHeight())
end

-- Slash Command Handlers
-- ======================
SLASH_LOOTAUCTIONEER1, SLASH_LOOTAUCTIONEER2 = "/lootauctioneer", "/la"
SlashCmdList["LOOTAUCTIONEER"] = function(msg, editbox) LootAuctioneer:AuctionHandler(msg, editbox) end
